
#include <BPainter>
#include <BImage>
#include <BStyle>
#include <BMouseEvent>
#include <BButton>
#include <BOpenGL>
#include <BLayout>

#include <BGroupBox>
#include "member_BGroupBox.h"

using namespace BWE;

#define member					(*(member_BGroupBox*)_ptr)
#define member_allocate()		_ptr = new member_BGroupBox(this)
#define member_release()		delete (member_BGroupBox*)_ptr

BGroupBox::BGroupBox(BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setPerch(15, 30, 15, 15);
}
BGroupBox::BGroupBox(const BString& title, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setPerch(15, 30, 15, 15);
	this->setTitle(title);
}
BGroupBox::~BGroupBox()
{
	member_release();
}

bool BGroupBox::setTitle(const BString& title)
{
	if (BWidget::setTitle(title))
	{
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
bool BGroupBox::setTitleIcon(BIcon* titleIcon)
{
	if (BWidget::setTitleIcon(titleIcon))
	{
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}

void BGroupBox::setForm(Form form)
{
	if (member.ia_form != form)
	{
		member.ia_form = form;
		member.dirty = true;
		this->adjust();
		this->fresh();
	}
}
Form BGroupBox::form() const
{
	return member.ia_form;
}

void BGroupBox::setAlign(Align align)
{
	if (member.ia_align != align)
	{
		member.ia_align = align;
		member.dirty = true;
		this->fresh();
	}
}
Align BGroupBox::align() const
{
	return member.ia_align;
}

void BGroupBox::setFlat(bool flat)
{
	if (member.flat != flat)
	{
		member.flat = flat;
		member.dirty = true;
		this->fresh();
	}
}
bool BGroupBox::flat() const
{
	return member.flat;
}

void BGroupBox::setWired(bool wired)
{
	if (member.wired != wired)
	{
		member.wired = wired;
		member.dirty = true;
		this->fresh();
	}
}
bool BGroupBox::wired() const
{
	return member.wired;
}

void BGroupBox::setCheckable(bool checkable)
{
	if (member.checkable != checkable)
	{
		member.checkable = checkable;
		member.checked = false;
		this->fresh();
	}
}
bool BGroupBox::checkable() const
{
	return member.checkable;
}

void BGroupBox::setChecked(bool checked)
{
	if (member.checkable)
	{
		if (member.checked != checked)
		{
			member.checked = checked;
			emit(Signal_Checked, checked);
			this->fresh();
		}
	}
}
bool BGroupBox::checked() const
{
	return member.checked;
}

void BGroupBox::setFoldable(bool foldable)
{
	if (member.foldable != foldable)
	{
		member.foldable = foldable;
		member.folded = false;
		this->fresh();
	}
}
bool BGroupBox::foldable() const
{
	return member.foldable;
}

void BGroupBox::setFolded(bool folded)
{
	if (!member.foldable)
		return;
	if (member.folded != folded)
	{
		member.folded = folded;
		if (folded)
		{
			if (BLayout* centerLayout = this->layout(Part_Center))
			{
				member.centerSize = centerLayout->preferSize();
				member.backupCenterState = centerLayout->enabled();
				centerLayout->setVisible(false);
			}
			if (BLayout* leftLayout = this->layout(Part_Left))
			{
				member.backupLeftState = leftLayout->enabled();
				leftLayout->setVisible(false);
			}
			if (BLayout* rightLayout = this->layout(Part_Right))
			{
				member.backupRightState = rightLayout->enabled();
				rightLayout->setVisible(false);
			}
			if (BLayout* bottomLayout = this->layout(Part_Bottom))
			{
				member.backupBottomState = bottomLayout->enabled();
				bottomLayout->setVisible(false);
			}
			member.backupPerch = this->perch();
			this->setPerch(Part_Bottom, 0);
			member.backupMargin = this->margin();
			this->setMargin(Part_Top | Part_Bottom, 0);
			member.backupVPolicy = this->heightPolicy();
			this->setHeightPolicy(Policy_Preferred);
		}
		else
		{
			if (BLayout* centerLayout = this->layout(Part_Center))
			{
				centerLayout->setVisible(member.backupCenterState);
			}
			if (BLayout* leftLayout = this->layout(Part_Left))
			{
				leftLayout->setVisible(member.backupLeftState);
			}
			if (BLayout* rightLayout = this->layout(Part_Right))
			{
				rightLayout->setVisible(member.backupRightState);
			}
			if (BLayout* bottomLayout = this->layout(Part_Bottom))
			{
				bottomLayout->setVisible(member.backupBottomState);
			}
			this->setPerch(member.backupPerch);
			this->setMargin(member.backupMargin);
			this->setHeightPolicy(member.backupVPolicy);
		}
		member.dirty = true;
		this->emit(Signal_Folded, member.folded);
		this->fresh();
	}
}
bool BGroupBox::folded() const
{
	return member.folded;
}

BSize BGroupBox::sizeHint() const
{
	if (member.dirty)
	{
		member.freshRects(this->realStyle());
		member.dirty = false;
	}
	int hintWidth = member.ia_hintSize.width();
	if (member.folded)
	{
		if (hintWidth < member.centerSize.width())
			hintWidth = member.centerSize.width();
	}
	return this->preferSize(hintWidth, 0);
}

void BGroupBox::resizeEvent(const BEvent& event)
{
	BWidget::resizeEvent(event);
	member.dirty = true;
}

void BGroupBox::styleEvent(const BEvent& event)
{
	const BStyle* style = this->realStyle();

	member.autoTop = style->value(Value_Perch_Top).empty();
	member.autoLeft = style->value(Value_Perch_Left).empty();
	member.autoRight = style->value(Value_Perch_Right).empty();
	member.autoBottom = style->value(Value_Perch_Bottom).empty();

	const BValue& formValue = style->value(Value_Form);
	if (formValue.valid())
		member.ia_form = formValue;

	const BValue& alignValue = style->value(Value_Align);
	if (alignValue.valid())
		member.ia_align = alignValue;

	const BValue& spaceValue = style->value(Value_Space);
	if (spaceValue.valid())
		member.ia_spacing = spaceValue;

	const BValue& wireSizeValue = style->value(Value_Wire_Size);
	if (wireSizeValue.valid())
		member.wireSize = wireSizeValue;

	const BValue& iconSizeValue = style->value(Value_Icon_Size);
	if (iconSizeValue.valid())
		member.iconSize = iconSizeValue;

	const BValue& flatValue = style->value(Value_Flat);
	if (flatValue.valid())
		member.flat = flatValue;

	const BValue& wiredValue = style->value(Value_Wired);
	if (wiredValue.valid())
		member.wired = wiredValue;

	member.dirty = true;
	BWidget::styleEvent(event);
}
void BGroupBox::freshEvent(const BEvent& event)
{
	BWidget::freshEvent(event);
	if (member.dirty)
	{
		const BStyle* style = this->realStyle();
		const BFont* font = style->font();
		if (!member.flat)
		{
			BPerch perch = this->perch();
			if (member.autoTop && perch.top() < font->size() && font)
				perch.top() = font->size();
			if (member.autoLeft)
				perch.left() = perch.top() / 2;
			if (member.autoRight)
				perch.right() = perch.top() / 2;
			if (member.autoBottom)
			{
				if (member.foldable && member.folded)
					perch.bottom() = 0;
				else
					perch.bottom() = perch.top() / 2;
			}
			if (member.folded)
			{
				member.backupPerch = perch;
				member.backupMargin = this->margin();
				this->setMargin(Part_Top | Part_Bottom, 0);
			}
			else
			{
				this->setPerch(perch);
			}
		}
		member.freshRects(style);
		member.dirty = false;
	}
}
void BGroupBox::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawPerch();
	painter.drawTitle();

	if (member.wired && painter.setColor(Color_Wire))
	{
		painter.setLineWidth(member.wireSize);
		if (member.flat || member.folded)
		{
			painter.drawLine(member.wirePoints.first(), member.wirePoints(1));
			painter.drawLine(member.wirePoints.last(), member.wirePoints(member.wirePoints.size() - 2));
		}
		else
		{
			painter.setLineSmooth(true);
			painter.drawLineStrip(member.wirePoints);
			painter.setLineSmooth(false);
		}
	}
	State state = member.state;
	if (!this->enabled())
		state = State_Disable;
	if (member.checkable && state)
	{
		if (member.checked)
			painter.setColor(Color_Perch_Checked);
		else if (state == State_Pressed)
			painter.setColor(Color_Perch_Pressed);
		else if (state == State_Hovered)
			painter.setColor(Color_Perch_Hovered);
		painter.fillRect(member.titleArea);
	}
	if (member.ia_imageRect.valid())
	{
		BRect imageRect = member.ia_imageRect;
		if (member.iconSize.empty())
			imageRect = this->rect(Part_Title).wrap(imageRect);
		if (BIcon* icon = this->titleIcon())
		{
			if (BImage* image = icon->image(state, member.checked))
			{
				painter.drawImage(imageRect, image);
			}
		}
		else if (member.checkable)
		{
			if (state == State_Disable)
				painter.setColor(Color_Arrow_Disable);
			else if (state == State_Hovered)
				painter.setColor(Color_Arrow_Hovered);
			else
				painter.setColor(Color_Arrow);
			painter.setLineSmooth(true);
			painter.setLineWidth(2);
			painter.drawRect(imageRect % 86);
			if (member.checked)
			{
				painter.drawGraph(imageRect % 66, Graph_Check);
			}
		}
	}
	if (!this->enabled())
		painter.setColor(Color_Text_Disable);
	else if (member.checked)
		painter.setColor(Color_Text_Checked);
	else
		painter.setColor(Color_Text);
	if (member.t_title.size())
		painter.drawText(member.ia_textRect, member.t_title);
	else
		painter.drawText(member.ia_textRect, this->title());
	painter.drawMargin();
	painter.drawBorder();
}

void BGroupBox::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (member.state != State_Normal)
	{
		member.state = State_Normal;
		this->fresh();
	}
	BWidget::mouseEnterEvent(mouseEvent);
}
void BGroupBox::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.state != State_Normal)
	{
		member.state = State_Normal;
		this->fresh();
	}
	BWidget::mouseLeaveEvent(mouseEvent);
}

void BGroupBox::mousePressEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	if (member.titleArea.contain(pos))
	{
		member.state = State_Pressed;
		this->fresh();
	}
	BWidget::mousePressEvent(mouseEvent);
}
void BGroupBox::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	if (member.state == State_Pressed)
	{
		member.state = member.titleArea.contain(pos) ? State_Hovered : State_Normal;
		this->fresh();
	}
	BWidget::mouseReleaseEvent(mouseEvent);
}
void BGroupBox::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_None)
	{
		const BPoint& pos = mouseEvent.pos();
		State state = member.titleArea.contain(pos) ? State_Hovered : State_Normal;
		if (member.state != state)
		{
			member.state = state;
			this->fresh();
		}
	}
	BWidget::mouseMoveEvent(mouseEvent);
}

void BGroupBox::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		const BPoint& pos = mouseEvent.pos();
		if (member.checkable && member.titleArea.contain(pos))
		{
			setChecked(!member.checked);
			emit(Signal_Toggled, member.checked);
		}
	}
	BWidget::mouseClickEvent(mouseEvent);
}
